home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip: 2005 Utilities
/
CHIP Utilities 2005.7z
/
CHIP Utilities 2005.iso
/
tools
/
convert.c
next >
Wrap
C/C++ Source or Header
|
2003-12-19
|
19KB
|
575 lines
/* CD Shell Image conversion tool.
* Copyright (C) 2002-2004 Michael K Ter Louw
*
* Version 1.1
* - Can convert both directions: bmp->csi and csi->bmp.
*
* Version 1.0
* - First release. Converts BMP files to CSI format.
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
/* Image conversion modes. */
#define BMP_2_CSI 1
#define CSI_2_BMP 2
/* Bitmap image format options. */
#define BMP_WINDOWS 0
#define BMP_OS2 1
/* Bitmap file headers. */
char bmp_file_header[] =
{ 'B', 'M', /* BMP signature */
0x36, 0x10, 0x0E, 0x00, /* File size */
0x00, 0x00, 0x00, 0x00, /* Reserved space */
0x36, 0x00, 0x00, 0x00 /* Image offset (for Windows format) */
};
char bmp_info_header[] =
{ 0x28, 0x00, 0x00, 0x00, /* Info header size (for Windows format) */
0x80, 0x02, 0x00, 0x00, /* Image width (640 pixels) */
0xE0, 0x01, 0x00, 0x00, /* Image height (480 pixels) */
0x01, 0x00, /* Number of planes */
0x18, 0x00 /* Color depth (24bpp) */
};
char bmp_extra_header[] =
{ 0x00, 0x00, 0x00, 0x00, /* Compression type (none) */
0x00, 0x10, 0x0E, 0x00, /* Image data size (921600 bytes) */
0x13, 0x0B, 0x00, 0x00, /* Horizontal pixels per meter */
0x13, 0x0B, 0x00, 0x00, /* Vertical pixels per meter */
0x00, 0x00, 0x00, 0x00, /* Number of colors used (i.e., palette size) */
0x00, 0x00, 0x00, 0x00 /* Number of important colors */
};
/* CSI Encoding flags. */
#define FL_BANK_TRIGGER_32 0x8000
#define FL_BANK_TRIGGER_24 0x4000
#define FL_SPLIT_PIXEL 0x2000
#define FL_ABSOLUTE_BLOCK 0x2000
#define FL_64KB_FILE_BOUNDARY 0x2000
/* CD Shell Image header. */
const char * csi_header[] =
{ "-CSI",
"CD Shell Image, v1.0",
"http://www.cdshell.org"
};
/* Pixel boundaries that trigger a bank switch in 24bpp mode. */
long bank_trigger_24[] =
{ 21845, 43690, 65536,
87381, 109226, 131072,
152917, 174762, 196608,
218453, 240298, 262144,
283989, 305834, 307200, /* Last value represents end of image. */
-1
};
/* Pixel boundaries that trigger a bank switch in 32bpp mode. */
long bank_trigger_32[] =
{ 16384, 32768, 49152, 65536,
81920, 98304, 114688, 131072,
147456, 163840, 180224, 196608,
212992, 229376, 245760, 262144,
278528, 294912,
-1
};
/* Image data buffer. Intermediate storage between decoding and encoding operations. */
unsigned char image_data[640 * 480 * 3];
const char error_bmp_general[] = "Error reading input file, or unexpected end of file.\n";
const char error_file_write[] = "Error writing to output file.\n";
/* Function prototypes. */
int process_command_line(int , char **, int *, char **, char **);
int decode_bmp(char *);
int encode_bmp(char *, int);
int decode_csi(char *);
int encode_csi(char *);
int simple_strnicmp(const char *, const char *, int);
/* Program entry procedure. */
int main(int argc, char **argv)
{ int conversion = 0;
char *infile = NULL;
char *outfile = NULL;
printf("\nCD Shell Image conversion utility, Version 1.1\n"
"Copyright (C) 2002-2004 Michael K Ter Louw\n"
"Visit http://www.cdshell.org for terms of use.\n\n");
if (process_command_line(argc, argv, &conversion, &infile, &outfile))
{ printf("\nSyntax: convert <-bmp2csi | -csi2bmp> <-o outputfile> <inputfile>\n");
return 1;
}
switch (conversion)
{ case BMP_2_CSI:
if (decode_bmp(infile)) return 1;
if (encode_csi(outfile)) return 1;
break;
case CSI_2_BMP:
if (decode_csi(infile)) return 1;
if (encode_bmp(outfile, BMP_WINDOWS)) return 1;
}
printf("Image encoded successfully.\n");
return 0;
}
/* Process command line arguments. */
int process_command_line(int argc, char *argv[], int *conversion, char **infile, char **outfile)
{ int h;
for (h = 1; h < argc; h++)
{ /* Check for input file. */
if (*(argv[h]) != '-')
{ if (*infile != NULL)
{ printf("Error: Input file already specified.\n");
return 1;
}
*infile = argv[h]; continue;
}
/* Handle command switches. */
if (!simple_strnicmp(argv[h], "-bmp2csi", 9))
{ if (*conversion)
{ printf("Conversion mode already specified.\n");
return 1;
}
*conversion = BMP_2_CSI;
continue;
}
if (!simple_strnicmp(argv[h], "-csi2bmp", 9))
{ if (*conversion)
{ printf("Conversion mode already specified.\n");
return 1;
}
*conversion = CSI_2_BMP;
continue;
}
if (!simple_strnicmp(argv[h], "-o", 3))
{ if (*outfile != NULL)
{ printf("Error: Output file already specified.\n");
return 1;
}
if (h == argc - 1)
{ printf("Error: Output file expected.\n");
return 1;
}
h++; *outfile = argv[h]; continue;
}
if (!simple_strnicmp(argv[h], "-v", 3))
{
}
printf("Error: Unrecognized option: \"%s\"\n", argv[h]);
return 1;
}
/* Check to see if parameters were not defined. */
if (!(*conversion))
{ printf("Error: Conversion mode not specified.\n");
return 1;
}
if (!(*infile))
{ printf("Error: Input file not specified.\n");
return 1;
}
if (!(*outfile))
{ printf("Error: Output file not specified.\n");
return 1;
}
return 0;
}
/* Decode a bitmap file. */
int decode_bmp(char * filename)
{ long buffer_index, h;
long image_offset = 14;
unsigned short p[2];
unsigned long q[2];
unsigned char c, d, e[2];
FILE * input_file;
/* Read BMP file header. */
if (!(input_file = fopen(filename, "rb")))
{ printf("Error opening input file: \"%s\"\n", filename);
return 1;
}
/* Verify BMP signature. */
if ((fread(e, 2, 1, input_file) < 1) || e[0] != bmp_file_header[0] ||
e[1] != bmp_file_header[1])
{ printf(error_bmp_general); fclose(input_file); return 1;
}
/* Determine image offset (different depending on Windows or OS/2 format. */
if (fseek(input_file, 14, SEEK_SET))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
if (fread(q, 4, 1, input_file) < 1)
{ printf(error_bmp_general); fclose(input_file); return 1;
}
image_offset += q[0];
/* Verify image width and height. */
if (fread(q, 4, 2, input_file) < 2)
{ printf(error_bmp_general); fclose(input_file); return 1;
}
if ((q[0] != 640) || (q[1] != 480))
{ printf("Error: BMP file is not 640 by 480.\n"); fclose(input_file); return 1;
}
/* Verify color depth. */
if (fread(p, 2, 2, input_file) < 2)
{ printf(error_bmp_general); fclose(input_file); return 1;
}
if (p[1] != 24)
{ printf("Error: BMP file is not 24 bits per pixel.\n"); fclose(input_file); return 1;
}
/* Read the image data. */
if (fseek(input_file, image_offset, SEEK_SET))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
printf("Bitmap image verified 640x480x24.\n");
buffer_index = 640 * 479 * 3;
for (h = 0; h < 480; buffer_index -= 640 * 3, h++)
{ if ((fread(image_data + buffer_index, 640 * 3, 1, input_file)) != 1)
{ printf(error_bmp_general); fclose(input_file); return 1;
}
}
fclose(input_file);
return 0;
}
/* Create a BMP image file. */
int encode_bmp(char * filename, int format)
{ int buffer_index;
int h, i;
FILE * output_file;
/* Open the output file. */
if (!(output_file = fopen(filename, "wb")))
{ printf("Couldn't open output file \"%s\".\n", filename);
return 1;
}
printf("Encoding Bitmap (BMP) Image...\n");
/* Generate header. */
if (format)
{ /* Set header values to OS/2 format. */
bmp_file_header[10] = 26;
bmp_info_header[0] = 12;
}
fwrite(bmp_file_header, 14, 1, output_file);
fwrite(bmp_info_header, 16, 1, output_file);
fwrite(bmp_extra_header, 24, 1, output_file);
for (h = 0; h < 480; h++)
{ buffer_index = 640 * 3 * (479 - h);
for (i = 0; i < 640 * 3; buffer_index++, i++)
fputc(image_data[buffer_index], output_file);
}
fclose(output_file);
return 0;
}
/* Decode a CD Shell Image (Version 1) file. */
int decode_csi(char * filename)
{ long buffer_index, block_code, file_pos = 0x40;
unsigned char c[6];
FILE * input_file;
/* Read CSI file header. */
if (!(input_file = fopen(filename, "rb")))
{ printf("Error opening input file: \"%s\"\n", filename);
return 1;
}
/* Verify CSI signature. */
if ((fread(c, 4, 1, input_file) < 1) || (c[0] != '-') ||
(c[1] != 'C') || (c[2] != 'S') || (c[3] != 'I'))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
/* Verify CSI version. */
if ((fread(c, 2, 1, input_file) < 1) || (c[0] != 1) || (c[1] != 0))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
printf("CD Shell Image format verified 640x480x24.\n");
/* Read the rest of the header. */
if ((fread(image_data, 0x40 - 6, 1, input_file) < 1))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
/* Decode encoded blocks. */
for (buffer_index = 0; 1; )
{ /* Read the block code. */
if ((fread(c, 2, 1, input_file) < 1))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
block_code = (long) (c[1] << 8) + c[0];
file_pos += 2;
/* Check for end of file reached. */
if (!block_code)
{ if (buffer_index != 640 * 480 * 3)
{ printf(error_bmp_general); fclose(input_file); return 1;
}
return 0;
}
/* Handle file-block position markers. */
if (block_code == FL_64KB_FILE_BOUNDARY)
{ if ((file_pos % 65536) && (fread(c, 65536 - (file_pos % 65536), 1, input_file) < 1))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
file_pos = (file_pos + 65535) & ~65535;
continue;
}
/* Handle 24bpp bank triggers. */
if (block_code & FL_BANK_TRIGGER_24)
{ if (block_code & FL_SPLIT_PIXEL)
{ if ((fread(image_data + buffer_index, 3, 1, input_file) < 1))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
buffer_index += 3; file_pos += 3;
}
continue;
}
/* Ignore 32bpp bank triggers. */
else if (block_code & FL_BANK_TRIGGER_32) continue;
/* Handle absolute blocks. */
if (block_code & FL_ABSOLUTE_BLOCK)
{ block_code ^= FL_ABSOLUTE_BLOCK;
file_pos += block_code * 3;
for (; block_code; block_code--)
{ if ((fread(image_data + buffer_index, 3, 1, input_file) < 1))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
buffer_index += 3;
}
continue;
}
/* Handle repeat blocks. */
else
{ if ((fread(c, 3, 1, input_file) < 1))
{ printf(error_bmp_general); fclose(input_file); return 1;
}
for (; block_code; block_code--)
{ image_data[buffer_index + 0] = c[0];
image_data[buffer_index + 1] = c[1];
image_data[buffer_index + 2] = c[2];
buffer_index += 3;
}
file_pos += 3;
continue;
}
}
fclose(input_file);
return 0;
}
/* Create a CD Shell Image file (Version 1 format). */
int encode_csi(char * filename)
{ int h, i;
int file_block_pos;
unsigned char r, g, b;
FILE * output_file;
long current_pixel = 0;
long file_pos;
long block_code;
long repeat_count;
long absolute_count;
if (!(output_file = fopen(filename, "wb")))
{ printf("Couldn't open output file \"%s\".\n", filename);
return 1;
}
printf("Encoding CD Shell Image (version 1)...\n");
/* Generate header. */
fwrite(csi_header[0], 4, 1, output_file);
block_code = 1; /* block_code = CD Shell Image version. */
fwrite(&block_code, 2, 1, output_file);
fwrite(csi_header[1], strlen(csi_header[1]) + 1, 1, output_file);
fwrite(csi_header[2], strlen(csi_header[2]) + 1, 1, output_file);
h = (int) (0x40 - (strlen(csi_header[1]) + strlen(csi_header[2]) + 8));
for (; h > 0; h--) fputc('\0', output_file);
file_pos = 0x40;
/* Generate encoded blocks. */
for (block_code = 0; 1; block_code = 0)
{ /* Terminate if end of image has been reached. */
if (current_pixel == 640 * 480) break;
/* Check for 64kb file boundary. */
file_block_pos = file_pos % 65536;
if (file_block_pos > (65536 - 7))
{ block_code = FL_64KB_FILE_BOUNDARY;
fwrite(&block_code, 2, 1, output_file); file_block_pos += 2;
for (; file_block_pos < 65536; file_block_pos++)
fputc('\0', output_file);
file_pos = (file_pos + 65535) & ~65535;
continue;
}
/* Check to see if the pixel lies on a bank boundary. */
for(i = 0; bank_trigger_24[i] != -1; i++)
{ if (bank_trigger_24[i] == current_pixel)
{ block_code |= FL_BANK_TRIGGER_24;
bank_trigger_24[i] = 0;
break;
}
}
for(i = 0; bank_trigger_32[i] != -1; i++)
{ if (bank_trigger_32[i] == current_pixel)
{ block_code |= FL_BANK_TRIGGER_32;
bank_trigger_32[i] = 0;
break;
}
}
/* Generate bank switch trigger block. */
if (block_code)
{ /* Check for pixel split across banks (only in 24bpp mode). */
if (current_pixel & 16383)
{ block_code |= FL_SPLIT_PIXEL;
fwrite(&block_code, 2, 1, output_file);
b = image_data[current_pixel * 3 + 0];
g = image_data[current_pixel * 3 + 1];
r = image_data[current_pixel * 3 + 2];
fwrite(&b, 1, 1, output_file);
fwrite(&g, 1, 1, output_file);
fwrite(&r, 1, 1, output_file);
current_pixel++; file_pos += 5;
}
/* No split pixel, just a nice clean bank switch. */
else
{ fwrite(&block_code, 2, 1, output_file);
file_pos += 2;
}
continue;
}
/* Check for repeated data. */
for (repeat_count = 1, i = current_pixel * 3; repeat_count < 8191; repeat_count++, i += 3)
{ /* Break loop if end of run found. */
if ((image_data[i + 0] != image_data[i + 3]) ||
(image_data[i + 1] != image_data[i + 4]) ||
(image_data[i + 2] != image_data[i + 5])) break;
/* Don't let repeat runs span across bank boundaries. */
for(h = 0;
(bank_trigger_24[h] != -1) &&
(bank_trigger_24[h] != current_pixel + repeat_count)
; h++);
if (bank_trigger_24[h] == current_pixel + repeat_count) break;
for(h = 0;
(bank_trigger_32[h] != -1) &&
(bank_trigger_32[h] != current_pixel + repeat_count)
; h++);
if (bank_trigger_32[h] == current_pixel + repeat_count) break;
}
/* Generate repeat block if repeated data was found. */
if (repeat_count > 1)
{ fwrite(&repeat_count, 2, 1, output_file);
b = image_data[current_pixel * 3 + 0];
g = image_data[current_pixel * 3 + 1];
r = image_data[current_pixel * 3 + 2];
fwrite(&b, 1, 1, output_file);
fwrite(&g, 1, 1, output_file);
fwrite(&r, 1, 1, output_file);
current_pixel += repeat_count; file_pos += 5;
continue;
}
/* No repeated data, so put together an absolute block. */
for (absolute_count = 1, i = current_pixel * 3; absolute_count < 8191; absolute_count++, i += 3)
{ /* Don't let absolute block span across 64kb file-block boundary. */
if ((file_block_pos + 2 + absolute_count * 3) > 65531) break;
/* Don't let absolute block span across bank boundaries. */
for(h = 0;
(bank_trigger_24[h] != -1) &&
(bank_trigger_24[h] != current_pixel + absolute_count)
; h++);
if (bank_trigger_24[h] == current_pixel + absolute_count) break;
for(h = 0;
(bank_trigger_32[h] != -1) &&
(bank_trigger_32[h] != current_pixel + absolute_count)
; h++);
if (bank_trigger_32[h] == current_pixel + absolute_count) break;
/* End absolute block if repeated data is found. */
if ((image_data[i + 0] != image_data[i + 3]) ||
(image_data[i + 1] != image_data[i + 4]) ||
(image_data[i + 2] != image_data[i + 5])) continue;
if ((image_data[i + 3] != image_data[i + 6]) ||
(image_data[i + 4] != image_data[i + 7]) ||
(image_data[i + 5] != image_data[i + 8])) continue;
break;
}
/* Output the absolute block to the file. */
block_code = absolute_count | FL_ABSOLUTE_BLOCK;
fwrite(&block_code, 2, 1, output_file);
for (h = 0, i = current_pixel * 3; h < absolute_count; h++, i += 3)
{ b = image_data[i + 0];
g = image_data[i + 1];
r = image_data[i + 2];
fwrite(&b, 1, 1, output_file);
fwrite(&g, 1, 1, output_file);
fwrite(&r, 1, 1, output_file);
}
current_pixel += absolute_count;
file_pos += (absolute_count * 3) + 2;
}
/* Write end of file-block code. */
block_code = 0;
fwrite(&block_code, 2, 1, output_file);
/* Done encoding the file. */
fclose(output_file);
return 0;
}
/* Included because strnicmp is not ANSI. */
int simple_strnicmp(const char source[], const char dest[], int max)
{ int h;
for (h = 0; h < max; h++)
{ if ((tolower(source[h]) != tolower(dest[h])) ||
((!source[h] || !dest[h]) && (h != (max - 1))))
return 1;
}
return 0;
}